{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "33370b62",
   "metadata": {},
   "source": [
    "| [01_base/09_函数和模块.ipynb](https://github.com/shibing624/python-tutorial/blob/master/01_base/09_函数和模块.ipynb)  | Python函数  |[Open In Colab](https://colab.research.google.com/github/shibing624/python-tutorial/blob/master/01_base/09_函数和模块.ipynb) |\n",
    "\n",
    "# 函数\n",
    "\n",
    "编程大师Martin Fowler先生曾经说过：“代码有很多种坏味道，重复是最坏的一种！”\n",
    "\n",
    "为了减少重复，函数登场。\n",
    "\n",
    "## 定义函数\n",
    "\n",
    "在Python中可以使用 def 关键字来定义函数，程序中函数的参数就相当于是数学上说的函数的自变量，可以通过 return 关键字来返回一个值，这相当于数学上说的函数的因变量。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 91,
   "id": "514dd9a6",
   "metadata": {},
   "outputs": [],
   "source": [
    "def add(a, b):\n",
    "    \"\"\"\n",
    "    add two nums\n",
    "    :param a: first num\n",
    "    :param b: second num\n",
    "    :return: result\n",
    "    \"\"\"\n",
    "    c = a + b\n",
    "    return c"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "45989159",
   "metadata": {},
   "source": [
    "## 使用函数\n",
    "\n",
    "使用函数时，只需要将参数换成特定的值传给函数。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 92,
   "id": "2028fae5",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "5\n",
      "foobar\n"
     ]
    }
   ],
   "source": [
    "# Python并没有限定参数的类型，因此可以使用不同的参数类型：\n",
    "print(add(2, 3))\n",
    "\n",
    "print(add('foo', 'bar'))  # foobar"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "3296c431",
   "metadata": {},
   "source": [
    "传入参数时，Python提供了两种选项，\n",
    "\n",
    "第一种是上面使用的按照位置传入参数，\n",
    "\n",
    "另一种则是使用关键词模式，显式地指定参数的值："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 93,
   "id": "745e8203",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "5"
      ]
     },
     "execution_count": 93,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "add(a=2, b=3)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 94,
   "id": "f098a9ca",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'goodmorning'"
      ]
     },
     "execution_count": 94,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "add(b='morning', a='good')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 95,
   "id": "5bedb112",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "5"
      ]
     },
     "execution_count": 95,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "add(2, b=3)  # 5"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "36e84edd",
   "metadata": {},
   "source": [
    "### 设定默认参数"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 96,
   "id": "73173e78",
   "metadata": {},
   "outputs": [],
   "source": [
    "def quad(x, a=1, b=0, c=0):\n",
    "    return a * x * x + b * x + c"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 97,
   "id": "8a2ed915",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "4.0"
      ]
     },
     "execution_count": 97,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "quad(2.0)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 98,
   "id": "8c6e0c60",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "10.0"
      ]
     },
     "execution_count": 98,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "quad(2.0, b=3)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "1ecbc317",
   "metadata": {},
   "source": [
    "### 接收不定参数\n",
    "\n",
    "使用如下方法，可以使函数接受不定数目的参数,类似java的..多个参数："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 99,
   "id": "c7786e48",
   "metadata": {},
   "outputs": [],
   "source": [
    "def add(x, *args):\n",
    "    total = x\n",
    "    for arg in args:\n",
    "        total += arg\n",
    "    return total"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e789607e",
   "metadata": {},
   "source": [
    "*args 表示参数数目不定，可以看成一个元组，\n",
    "\n",
    "把第一个参数后面的参数当作元组中的元素。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 100,
   "id": "b00ebc32",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "15\n",
      "3\n"
     ]
    }
   ],
   "source": [
    "print(add(1, 2, 3, 4, 5))  # 15\n",
    "print(add(1, 2))  # 3"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "d7c97481",
   "metadata": {},
   "source": [
    "### 使用关键词传入参数"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 101,
   "id": "96101950",
   "metadata": {},
   "outputs": [],
   "source": [
    "def add(x, **kwargs):\n",
    "    total = x\n",
    "    for arg, val in kwargs.items():\n",
    "        print(\"adding \", arg)\n",
    "        total += val\n",
    "    return total"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e4b87e69",
   "metadata": {},
   "source": [
    "**kwargs 表示参数数目不定，相当于一个字典，关键词和值对应于键值对。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 102,
   "id": "0ad41012",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "adding  a\n",
      "adding  b\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "6"
      ]
     },
     "execution_count": 102,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "add(1, a=2, b=3)  # 6"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 103,
   "id": "f352e28d",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "(2, 3) {'a': 'bar', 'b': 10}\n"
     ]
    }
   ],
   "source": [
    "# 可以接收任意数目的位置参数和键值对参数：\n",
    "def fun1(*args, **kwargs):\n",
    "    print(args, kwargs)\n",
    "    \n",
    "fun1(2, 3, a=\"bar\", b=10)  # (2, 3) {'a': u'bar', 'b': 10}"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c97cedaf",
   "metadata": {},
   "source": [
    "### 返回多个值"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 104,
   "id": "dc47edfc",
   "metadata": {},
   "outputs": [],
   "source": [
    "# 函数可以返回多个值：\n",
    "def to_val(x, y):\n",
    "    r = (x ** 2 + y ** 2) ** 0.5\n",
    "    total = x + y\n",
    "    return r, total"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 105,
   "id": "cdf6efab",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "5.0 7\n"
     ]
    }
   ],
   "source": [
    "a, b = to_val(3, 4)\n",
    "print(a, b)  # 5.0 7"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 106,
   "id": "7b8a4235",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "(5.0, 7)\n"
     ]
    }
   ],
   "source": [
    "# 事实上，Python将返回的两个值变成了元组：\n",
    "print(to_val(3, 4))  # (5.0, 7)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 107,
   "id": "80414771",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1 2 3\n"
     ]
    }
   ],
   "source": [
    "# 列表也有相似的功能,可以用来赋值：\n",
    "a, b, c = [1, 2, 3]\n",
    "print(a, b, c)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 108,
   "id": "e14b6287",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "5\n"
     ]
    }
   ],
   "source": [
    "# 可以将参数用元组传入：\n",
    "def add(a, b):\n",
    "    return a + b\n",
    "\n",
    "c = (2, 3)\n",
    "print(add(*c))  # 5\n",
    "# 这里的*必须要。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 109,
   "id": "378f7e18",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "7\n"
     ]
    }
   ],
   "source": [
    "# 还可以用字典传入参数哦：\n",
    "d = {'a': 2, 'b': 5}\n",
    "print(add(**d))  # 7"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "aa0ede08",
   "metadata": {},
   "source": [
    "### map 方法生成序列\n",
    "map函数\n",
    "\n",
    "map() 会根据提供的函数对指定序列做映射。\n",
    "\n",
    "map(aFun, aSeq)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 110,
   "id": "6a6bafc1",
   "metadata": {},
   "outputs": [],
   "source": [
    "def sqr(x):\n",
    "    return x ** 2"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 111,
   "id": "f2abbb7f",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "map"
      ]
     },
     "execution_count": 111,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "a = [2, 3, 4]\n",
    "result = map(sqr, a)  # [4,9,16]\n",
    "type(result)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 112,
   "id": "f82d5f32",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[4, 9, 16]"
      ]
     },
     "execution_count": 112,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# map返回的是个迭代器对象, 可以转化为list显示\n",
    "\n",
    "list(result)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "0dfffa8d",
   "metadata": {},
   "source": [
    "事实上，根据函数参数的多少，map 可以接受多组序列，\n",
    "将其对应的元素作为参数传入函数：\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 113,
   "id": "988c99dc",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[4, 9, 16]"
      ]
     },
     "execution_count": 113,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "def add(a, b):\n",
    "    return a + b\n",
    "\n",
    "a = [2, 3, 4]\n",
    "list(map(sqr, a))  # [4,9,16]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 114,
   "id": "0fd719f7",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[12, 14, 19]"
      ]
     },
     "execution_count": 114,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "a = (2, 3, 4)\n",
    "b = [10, 11, 15]\n",
    "list(map(add, a, b))  # [12, 14, 19]"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "84e420f7",
   "metadata": {},
   "source": [
    "# 模块\n",
    "\n",
    "用模块管理函数，Python中每个文件就代表了一个模块（module），\n",
    "\n",
    "Python会将所有 .py 结尾的文件认定为Python代码文件。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "18282785",
   "metadata": {},
   "source": [
    "在使用函数的时候我们通过import关键字导入指定的模块：\n",
    "\n",
    "`module1.py`\n",
    "\n",
    "```python\n",
    "def foo():\n",
    "    print('hello, world!')\n",
    "\n",
    "```\n",
    "\n",
    "`module2.py`\n",
    "```python\n",
    "def foo():\n",
    "    print('goodbye, world!')\n",
    "```\n",
    "\n",
    "`test.py`\n",
    "```python\n",
    "from module1 import foo\n",
    "\n",
    "# 输出hello, world!\n",
    "foo()\n",
    "\n",
    "from module2 import foo\n",
    "\n",
    "# 输出goodbye, world!\n",
    "foo()\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "47fb46c3",
   "metadata": {},
   "source": [
    "### __name__ 属性\n",
    "有时候我们想将一个 .py 文件既当作脚本，又能当作模块用，\n",
    "这个时候可以使用 __name__ 这个属性。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c63cd3c8",
   "metadata": {},
   "source": [
    "```python\n",
    "PI = 3.14\n",
    "\n",
    "\n",
    "def get_sum(lst):\n",
    "    \"\"\"\n",
    "    Sum the values in the list\n",
    "    :param lst:\n",
    "    :return:\n",
    "    \"\"\"\n",
    "    total = 0\n",
    "    for v in lst:\n",
    "        total = total + v\n",
    "    return total\n",
    "\n",
    "```\n",
    "\n",
    "上文保存为ex.py\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 119,
   "id": "f14980a2",
   "metadata": {},
   "outputs": [],
   "source": [
    "with open('ex.py', 'w') as f:\n",
    "    f.write(\"\"\"\n",
    "PI = 3.14\n",
    "def get_sum(lst):\n",
    "    total = 0\n",
    "    for v in lst:\n",
    "        total = total + v\n",
    "    return total\n",
    "    \"\"\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ed11afad",
   "metadata": {},
   "source": [
    "使用 ! 调用shell命令："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 120,
   "id": "c04ea3a5",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\r\n",
      "PI = 3.14\r\n",
      "def get_sum(lst):\r\n",
      "    total = 0\r\n",
      "    for v in lst:\r\n",
      "        total = total + v\r\n",
      "    return total\r\n",
      "    "
     ]
    }
   ],
   "source": [
    "!cat ex.py"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "b8933eb2",
   "metadata": {},
   "source": [
    "可以从ex模块中导入函数get_sum和变量："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 121,
   "id": "5bb6c5cb",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "3.14\n",
      "5\n"
     ]
    }
   ],
   "source": [
    "from ex import PI, get_sum\n",
    "\n",
    "print(PI)  # 3.14\n",
    "print(get_sum([2, 3]))  # 5\n",
    "\n",
    "# 可以使用 * 导入所有变量, 不提倡，因为可能覆盖一些已有的函数"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 122,
   "id": "6b513564",
   "metadata": {},
   "outputs": [],
   "source": [
    "# 删除文件：\n",
    "import os\n",
    "\n",
    "os.remove('ex.py')"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "814bca7b",
   "metadata": {},
   "source": [
    "本节完。"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.8.8"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}